1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use msg_tool_macro::*;
9use serde::{Deserialize, Serialize};
10use std::io::{Read, Seek, Write};
11
12#[derive(Debug)]
13pub struct YSCFGBuilder {}
14
15impl YSCFGBuilder {
16 pub const fn new() -> Self {
18 YSCFGBuilder {}
19 }
20}
21
22impl ScriptBuilder for YSCFGBuilder {
23 fn default_encoding(&self) -> Encoding {
24 Encoding::Cp932
25 }
26
27 fn build_script(
28 &self,
29 buf: Vec<u8>,
30 _filename: &str,
31 encoding: Encoding,
32 _archive_encoding: Encoding,
33 config: &ExtraConfig,
34 _archive: Option<&Box<dyn Script>>,
35 ) -> Result<Box<dyn Script + Send + Sync>> {
36 Ok(Box::new(YSCFG::new(MemReader::new(buf), encoding, config)?))
37 }
38
39 fn extensions(&self) -> &'static [&'static str] {
40 &["ybn"]
41 }
42
43 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
44 if buf_len >= 4 && buf.starts_with(b"YSCF") {
45 return Some(20);
46 }
47 None
48 }
49
50 fn script_type(&self) -> &'static ScriptType {
51 &ScriptType::YurisYSCFG
52 }
53
54 fn can_create_file(&self) -> bool {
55 true
56 }
57
58 fn create_file<'a>(
59 &'a self,
60 filename: &'a str,
61 writer: Box<dyn WriteSeek + 'a>,
62 encoding: Encoding,
63 file_encoding: Encoding,
64 config: &ExtraConfig,
65 ) -> Result<()> {
66 create_file(
67 filename,
68 writer,
69 encoding,
70 file_encoding,
71 config.custom_yaml,
72 )
73 }
74}
75
76#[derive(Debug, StructPack, StructUnpack, Deserialize, Serialize)]
77struct YSCFGData {
78 engine: u32,
79 unk0: u32,
80 compile: u32,
81 screen_width: u32,
82 screen_height: u32,
83 enable: u32,
84 image_type_slots: [u8; 8],
85 sound_type_slots: [u8; 4],
86 thread: u32,
87 debug_mode: u32,
88 sound: u32,
89 window_resize: u32,
90 window_frame: u32,
91 file_priority_dev: u32,
92 file_priority_debug: u32,
93 file_priority_release: u32,
94 unk1: u32,
95 #[skip_pack_if(self.engine < 500)]
97 #[skip_unpack_if(engine < 500)]
98 unk2: u32,
99 #[skip_pack_if(self.engine < 500)]
100 #[skip_unpack_if(engine < 500)]
101 unk3: u32,
102 #[skip_pack_if(self.engine < 500)]
103 #[skip_unpack_if(engine < 500)]
104 unk4: u32,
105 #[pstring(u16)]
106 caption: String,
107}
108
109#[derive(Debug)]
110pub struct YSCFG {
111 data: YSCFGData,
112 custom_yaml: bool,
113}
114
115impl YSCFG {
116 pub fn new<T: Read + Seek>(
117 mut reader: T,
118 encoding: Encoding,
119 config: &ExtraConfig,
120 ) -> Result<Self> {
121 let mut sig = [0; 4];
122 reader.read_exact(&mut sig)?;
123 if &sig != b"YSCF" {
124 anyhow::bail!("Unsupported YSCFG file.");
125 }
126 let data = YSCFGData::unpack(&mut reader, false, encoding, &None)?;
127 Ok(Self {
128 data,
129 custom_yaml: config.custom_yaml,
130 })
131 }
132}
133
134impl Script for YSCFG {
135 fn default_output_script_type(&self) -> OutputScriptType {
136 OutputScriptType::Custom
137 }
138
139 fn is_output_supported(&self, output: OutputScriptType) -> bool {
140 matches!(output, OutputScriptType::Custom)
141 }
142
143 fn default_format_type(&self) -> FormatOptions {
144 FormatOptions::None
145 }
146
147 fn custom_output_extension(&self) -> &'static str {
148 if self.custom_yaml { "yaml" } else { "json" }
149 }
150
151 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
152 let s = if self.custom_yaml {
153 serde_yaml_ng::to_string(&self.data)
154 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
155 } else {
156 serde_json::to_string_pretty(&self.data)
157 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
158 };
159 let mut writer = crate::utils::files::write_file(filename)?;
160 let s = encode_string(encoding, &s, false)?;
161 writer.write_all(&s)?;
162 writer.flush()?;
163 Ok(())
164 }
165
166 fn custom_import<'a>(
167 &'a self,
168 custom_filename: &'a str,
169 file: Box<dyn WriteSeek + 'a>,
170 encoding: Encoding,
171 output_encoding: Encoding,
172 ) -> Result<()> {
173 create_file(
174 custom_filename,
175 file,
176 encoding,
177 output_encoding,
178 self.custom_yaml,
179 )
180 }
181}
182
183fn create_file<'a>(
184 custom_filename: &'a str,
185 mut writer: Box<dyn WriteSeek + 'a>,
186 encoding: Encoding,
187 output_encoding: Encoding,
188 yaml: bool,
189) -> Result<()> {
190 let input = crate::utils::files::read_file(custom_filename)?;
191 let s = decode_to_string(output_encoding, &input, true)?;
192 let data: YSCFGData = if yaml {
193 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
194 } else {
195 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
196 };
197 writer.write_all(b"YSCF")?;
198 data.pack(&mut writer, false, encoding, &None)?;
199 Ok(())
200}